#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
会吐槽的键盘 - Ubuntu专属版
为Linux系统优化的幽默键盘吐槽程序
使用状态匹配检测连续按键
"""

import os
import sys
import time
import threading
import json
import logging
import subprocess
from datetime import datetime
from pathlib import Path
from collections import defaultdict
from enum import Enum

# 第三方库导入
try:
    from pynput import keyboard
    import pygame
except ImportError as e:
    print(f"缺少依赖库: {e}")
    print("请运行: pip install pynput pygame")
    sys.exit(1)

class State(Enum):
    TAB = 0
    F5 = 1
    ENTER = 2
    CAPSLOCK = 3
    BACKSPACE = 4
    CTRLS = 5
    CTRLC = 6
    CTRLF = 7
    CTRLV = 8
    CTRLZ = 9
    CTRLQ = 10
    ALTF4 = 11
    CTRLALTT = 12
    SPACE = 13
    GAREN_Q = 14
    GAREN_W = 15
    GAREN_E = 16
    GAREN_R = 17
    NONE = 18  # 无状态
    
class TalkingKeyboardUbuntu:
    def __init__(self):
        self.setup_logging()
        self.setup_directories()
        self.check_ubuntu_dependencies()
        self.setup_audio()
        self.running = False
        self.last_audio_time = 0
        self.current_modifiers = set()
        
        self.current_state = State.NONE
        self.last_state = State.NONE
          
        self.config = self.load_config()
        self.key_taunts = {
            'startup': ['startup.mp3', 'startup.wav'],
            'tab': ['tab_taunt1.mp3', 'tab_taunt1.wav'],
            'f5': ['f5_taunt1.mp3', 'f5_taunt1.wav'],
            'enter': ['enter_taunt1.mp3', 'enter_taunt1.wav'],
            'caps_lock': ['caps_taunt1.mp3', 'caps_taunt1.wav'],
            'backspace': ['backspace_taunt1.mp3', 'backspace_taunt1.wav'],
            'space': ['space.mp3', 'space.wav'],
            'ctrl+s': ['ctrls_taunt1.mp3', 'ctrls_taunt1.wav'],
            'ctrl+c': ['ctrlc_taunt1.mp3', 'ctrlc_taunt1.wav'],
            'ctrl+v': ['ctrlv_taunt1.mp3', 'ctrlv_taunt1.wav'],
            'ctrl+z': ['ctrlz_taunt1.mp3', 'ctrlz_taunt1.wav'],
            'ctrl+f': ['ctrlf_taunt1.mp3', 'ctrlf_taunt1.wav'],
            'alt+f4': ['altf4_taunt1.mp3', 'altf4_taunt1.wav'],
            'ctrl+q': ['ctrlq_taunt1.mp3', 'ctrlq_taunt1.wav'],
            'ctrl+alt+t': ['terminal_taunt1.mp3', 'terminal_taunt1.wav'],
           'garen_q': ['garen_q.mp3', 'garen_q.wav'],
           'garen_w': ['garen_w.mp3', 'garen_w.wav'],
           'garen_e': ['garen_e.mp3', 'garen_e.wav'],
           'garen_r': ['garen_r.mp3', 'garen_r.wav']
        }
        
        self.key_state_map = {
            'tab': State.TAB,
            'f5': State.F5,
            'enter': State.ENTER,
            'caps_lock': State.CAPSLOCK,
            'backspace': State.BACKSPACE,
            'space': State.SPACE,
            'ctrl+s': State.CTRLS,
            'ctrl+c': State.CTRLC,
            'ctrl+v': State.CTRLV,
            'ctrl+f': State.CTRLF,
            'ctrl+z': State.CTRLZ,
            'ctrl+q': State.CTRLQ,
            'ctrl+alt+t': State.CTRLALTT,
            'alt+f4': State.ALTF4,
            'garen_q': State.GAREN_Q,
            'garen_w': State.GAREN_W,
            'garen_e': State.GAREN_E,
            'garen_r': State.GAREN_R
        }
        
        self.stats = {
            'total_taunts': 0,
            'key_taunts': defaultdict(int),
            'last_reset': datetime.now().date().isoformat()
        }
        
        self.load_stats()
        self.check_audio_files()
        self.preload_audio_files()
        
        logging.info("Ubuntu吐槽键盘初始化完成")
        
    def check_ubuntu_dependencies(self):
        print("🔍 检查Ubuntu系统依赖...")
        
        try:
            result = subprocess.run(['which', 'pulseaudio'], capture_output=True, text=True)
            if result.returncode != 0:
                print("⚠️  PulseAudio未安装，音频可能无法工作")
                print("   运行: sudo apt install pulseaudio")
            else:
                print("✅ PulseAudio已安装")
        except Exception as e:
            print(f"❌ 检查音频系统失败: {e}")

    def setup_audio(self):
        max_retries = 3
        for attempt in range(max_retries):
            try:
                if pygame.mixer.get_init():
                    pygame.mixer.quit()
                    time.sleep(0.2)
                
                os.environ['SDL_AUDIODRIVER'] = 'pulse'
                pygame.mixer.init(frequency=44100, size=-16, channels=2, buffer=2048)
                
                if pygame.mixer.get_init():
                    print(f"✅ Ubuntu音频系统初始化成功 (尝试 {attempt + 1}/{max_retries})")
                    return
                else:
                    print(f"⚠️ 音频系统初始化失败 (尝试 {attempt + 1}/{max_retries})")
                    
            except Exception as e:
                print(f"❌ 音频初始化错误 (尝试 {attempt + 1}/{max_retries}): {e}")
            
            time.sleep(0.5)
        
        print("❌ 音频系统初始化完全失败，程序将继续运行但可能没有声音")

    def setup_logging(self):
        log_dir = Path.home() / '.speaking_keyboard'
        log_dir.mkdir(exist_ok=True)
        
        logging.basicConfig(
            level=logging.INFO,
            format='%(asctime)s - %(levelname)s - %(message)s',
            handlers=[
                logging.FileHandler(log_dir / 'speaking_keyboard.log'),
                logging.StreamHandler(sys.stdout)
            ]
        )

    def setup_directories(self):
        self.data_dir = Path.home() / '.speaking_keyboard'
        self.data_dir.mkdir(exist_ok=True)
        
        self.audio_dir = Path(__file__).parent / 'audio'
        self.audio_dir.mkdir(exist_ok=True)
        
        self.config_file = self.data_dir / 'config.json'
        self.stats_file = self.data_dir / 'stats.json'

    def load_config(self):
        default_config = {
            'enabled': True,
            'volume': 0.7,
            'cooldown': 0.3,
            'random_taunts': True,
            'ubuntu_optimized': True
        }
        
        if self.config_file.exists():
            try:
                with open(self.config_file, 'r') as f:
                    user_config = json.load(f)
                    default_config.update(user_config)
            except Exception as e:
                logging.error(f"配置加载失败: {e}")
        
        return default_config

    def save_config(self):
        try:
            with open(self.config_file, 'w') as f:
                json.dump(self.config, f, indent=2)
        except Exception as e:
            logging.error(f"配置保存失败: {e}")

    def load_stats(self):
        if self.stats_file.exists():
            try:
                with open(self.stats_file, 'r') as f:
                    saved_stats = json.load(f)
                    if saved_stats.get('last_reset') == self.stats['last_reset']:
                        self.stats.update(saved_stats)
            except Exception as e:
                logging.error(f"统计加载失败: {e}")

    def save_stats(self):
        try:
            with open(self.stats_file, 'w') as f:
                json.dump(self.stats, f, indent=2, default=int)
        except Exception as e:
            logging.error(f"统计保存失败: {e}")

    def check_audio_files(self):
        missing_files = []

        for action, files in self.key_taunts.items():
            found = False
            for file_name in files:
                file_path = self.audio_dir / file_name
                if file_path.exists():
                    found = True
                    print(f"✅ 找到音频文件: {file_name}")
                    break

            if not found:
                missing_files.append(f"{action}: {files[0]}")

        if missing_files:
            print("⚠️  缺少以下音频文件:")
            for file_name in missing_files:
                print(f"   - {file_name}")
            print(f"\n请将音频文件放在: {self.audio_dir}")
            print("支持格式: MP3, WAV")
        else:
            print("✅ 所有音频文件检查完成")
       
    def preload_audio_files(self):
        print("🔄 预加载音频文件...")
        self.loaded_sounds = {}
        loaded_count = 0
        
        for action, files in self.key_taunts.items():
            audio_file = None
            for file_name in files:
                file_path = self.audio_dir / file_name
                if file_path.exists():
                    audio_file = file_path
                    break
            
            if audio_file:
                try:
                    if not pygame.mixer.get_init():
                        print("❌ 音频系统未初始化，无法预加载音频")
                        return
                    
                    file_size = audio_file.stat().st_size
                    if file_size == 0:
                        print(f"⚠️ 音频文件为空: {action} -> {audio_file.name}")
                        continue
                    
                    sound = pygame.mixer.Sound(str(audio_file))
                    self.loaded_sounds[action] = sound
                    loaded_count += 1
                    print(f"✅ 预加载: {action} -> {audio_file.name} ({file_size} bytes)")
                except Exception as e:
                    print(f"❌ 预加载失败: {action}: {e}")
                    print(f"   文件路径: {audio_file}")
                    print(f"   文件大小: {audio_file.stat().st_size if audio_file.exists() else 'N/A'} bytes")
                    logging.error(f"音频预加载失败: {action}: {e}")
        
        print(f"✅ 音频预加载完成，共加载 {loaded_count} 个音频文件")
    
    def play_taunt(self, key_type):
        if not self.config['enabled']:
            return
            
        if not pygame.mixer.get_init():
            print(f"❌ 音频系统未初始化，无法播放: {key_type}")
            return
            
        current_time = time.time()
        if current_time - self.last_audio_time < self.config['cooldown']:
            return
        
        print(f"🎵 播放: {key_type}")
        
        if key_type in self.loaded_sounds:
            try:
                sound = self.loaded_sounds[key_type]
                sound.set_volume(self.config['volume'])
                channel = sound.play()
                
                if channel:
                    print(f"✅ 使用预加载音频播放: {key_type}")
                    self.stats['total_taunts'] += 1
                    if key_type not in self.stats['key_taunts']:
                        self.stats['key_taunts'][key_type] = 0
                    self.stats['key_taunts'][key_type] += 1
                    self.last_audio_time = current_time
                    logging.info(f"播放吐槽: {key_type}")
                    return
                else:
                    print(f"❌ 预加载音频播放失败: {key_type}")
            except Exception as e:
                print(f"❌ 预加载音频播放错误: {key_type}: {e}")
                print(f"   异常类型: {type(e).__name__}")
                print(f"   异常详情: {repr(e)}")
                logging.error(f"预加载音频播放错误: {key_type}: {e}")
        
        audio_files = self.key_taunts.get(key_type, [])
        if not audio_files:
            print(f"❌ 未找到音频文件映射: {key_type}")
            return
            
        audio_file = None
        for file_name in audio_files:
            file_path = self.audio_dir / file_name
            if file_path.exists():
                audio_file = file_path
                break

        if not audio_file:
            print(f"❌ 音频文件不存在: {key_type}")
            return
            
        def play_thread():
            try:
                sound = pygame.mixer.Sound(str(audio_file))
                sound.set_volume(self.config['volume'])
                channel = sound.play()
                
                if channel:
                    print(f"✅ 从文件加载播放: {key_type}")
                    self.stats['total_taunts'] += 1
                    if key_type not in self.stats['key_taunts']:
                        self.stats['key_taunts'][key_type] = 0
                    self.stats['key_taunts'][key_type] += 1
                    
                    while channel.get_busy():
                        time.sleep(0.1)
                    
                    logging.info(f"播放吐槽: {key_type}")
                else:
                    print(f"❌ 文件音频播放失败: {key_type}")
                
            except Exception as e:
                logging.error(f"播放失败 {key_type}: {e}")
                print(f"❌ 播放失败 {key_type}: {e}")
                print(f"   异常类型: {type(e).__name__}")
                print(f"   异常详情: {repr(e)}")
                
        thread = threading.Thread(target=play_thread, daemon=True)
        thread.start()
        self.last_audio_time = current_time

    def play_taunt_and_wait(self, key_type):
        if not self.config['enabled']:
            return
            
        if not pygame.mixer.get_init():
            print(f"❌ 音频系统未初始化，无法播放: {key_type}")
            return
            
        current_time = time.time()
        
        print(f"🎵 播放: {key_type}")
        
        if key_type in self.loaded_sounds:
            try:
                sound = self.loaded_sounds[key_type]
                sound.set_volume(self.config['volume'])
                channel = sound.play()
                
                if channel:
                    print(f"✅ 使用预加载音频播放: {key_type}")
                    while channel.get_busy():
                        time.sleep(0.1)
                    print(f"✅ 音频播放完成: {key_type}")
                    self.stats['total_taunts'] += 1
                    if key_type not in self.stats['key_taunts']:
                        self.stats['key_taunts'][key_type] = 0
                    self.stats['key_taunts'][key_type] += 1
                    self.last_audio_time = current_time
                    logging.info(f"播放吐槽: {key_type}")
                    return
                else:
                    print(f"❌ 预加载音频播放失败: {key_type}")
            except Exception as e:
                print(f"❌ 预加载音频播放错误: {key_type}: {e}")
                print(f"   异常类型: {type(e).__name__}")
                print(f"   异常详情: {repr(e)}")
                logging.error(f"预加载音频播放错误: {key_type}: {e}")
        
        audio_files = self.key_taunts.get(key_type, [])
        if not audio_files:
            print(f"❌ 未找到音频文件映射: {key_type}")
            return
            
        audio_file = None
        for file_name in audio_files:
            file_path = self.audio_dir / file_name
            if file_path.exists():
                audio_file = file_path
                break

        if not audio_file:
            print(f"❌ 音频文件不存在: {key_type}")
            return
            
        try:
            sound = pygame.mixer.Sound(str(audio_file))
            sound.set_volume(self.config['volume'])
            channel = sound.play()
            
            if channel:
                print(f"✅ 从文件加载播放: {key_type}")
                while channel.get_busy():
                    time.sleep(0.1)
                print(f"✅ 音频播放完成: {key_type}")
                self.stats['total_taunts'] += 1
                if key_type not in self.stats['key_taunts']:
                    self.stats['key_taunts'][key_type] = 0
                self.stats['key_taunts'][key_type] += 1
                logging.info(f"播放吐槽: {key_type}")
            else:
                print(f"❌ 文件音频播放失败: {key_type}")
            
        except Exception as e:
            logging.error(f"播放失败 {key_type}: {e}")
            print(f"❌ 播放失败 {key_type}: {e}")
            print(f"   异常类型: {type(e).__name__}")
            print(f"   异常详情: {repr(e)}")
        
        self.last_audio_time = current_time

    def on_press(self, key):
        if not self.config['enabled']:
            return
            
        try:
            if hasattr(key, 'name') and any(mod in key.name for mod in ['ctrl', 'alt', 'shift', 'cmd']):
                self.current_modifiers.add(key)
                return

            key_name = None
            modifiers = self.get_current_modifiers()
            
            if hasattr(key, 'char') and key.char is not None:
                char = key.char.lower()
                
                if 'ctrl' in modifiers and char == 's':
                    self.handle_key_press('ctrl+s', State.CTRLS)
                elif 'ctrl' in modifiers and char == 'c':
                    self.handle_key_press('ctrl+c', State.CTRLC)
                elif 'ctrl' in modifiers and char == 'v':
                    self.handle_key_press('ctrl+v', State.CTRLV)
                elif 'ctrl' in modifiers and char == 'z':
                    self.handle_key_press('ctrl+z', State.CTRLZ)
                elif 'ctrl' in modifiers and char == 'f':
                    self.handle_key_press('ctrl+f', State.CTRLF)
                elif 'ctrl' in modifiers and char == 'q':
                    self.handle_key_press('ctrl+q', State.CTRLQ)
                elif 'Q' in modifiers or char == 'q':
                    self.handle_key_press('garen_q', State.GAREN_Q)
                elif 'W' in modifiers or char == 'w':
                    self.handle_key_press('garen_w', State.GAREN_W)
                elif 'E' in modifiers or char == 'e':
                    self.handle_key_press('garen_e', State.GAREN_E)
                elif 'R' in modifiers or char == 'r':
                    self.handle_key_press('garen_r', State.GAREN_R)
                    
            elif hasattr(key, 'name'):
                key_name = key.name.lower()
    
                if key_name in ['tab', 'f5', 'enter', 'caps_lock', 'backspace', 'space']:
                    self.handle_key_press(key_name, self.key_state_map[key_name])
                
                if 'ctrl' in modifiers and 'alt' in modifiers and key_name == 't':
                    self.handle_key_press('ctrl+alt+t', State.CTRLALTT)
                elif 'alt' in modifiers and key_name == 'f4':
                    self.handle_key_press('alt+f4', State.ALTF4)
                    
        except Exception as e:
            logging.error(f"按键处理错误: {e}")
            print(f"❌ 按键处理错误: {e}")

    def handle_key_press(self, key_type, new_state):
        self.current_state = new_state
        if self.current_state != self.last_state:
            self.play_taunt(key_type)
            self.last_state = self.current_state


    def on_release(self, key):
        if hasattr(key, 'name') and any(mod in key.name for mod in ['ctrl', 'alt', 'shift', 'cmd']):
            self.current_modifiers.discard(key)

    def get_current_modifiers(self):
        modifiers = set()
        for mod in self.current_modifiers:
            if hasattr(mod, 'name'):
                if 'ctrl' in mod.name:
                    modifiers.add('ctrl')
                elif 'alt' in mod.name:
                    modifiers.add('alt')
                elif 'shift' in mod.name:
                    modifiers.add('shift')
        return modifiers

    def start_listening(self):
        logging.info("启动键盘监听...")
        
        keyboard_listener = keyboard.Listener(
            on_press=self.on_press,
            on_release=self.on_release
        )
        
        keyboard_listener.daemon = True
        keyboard_listener.start()
        return keyboard_listener

    def start_control_monitoring(self):
        def on_toggle():
            self.config['enabled'] = not self.config['enabled']
            status = "启用" if self.config['enabled'] else "禁用"
            print(f"键盘吐槽{status}")
            self.save_config()

        def on_quit():
            print("吐槽键盘退出服务")
            self.running = False
            self.save_stats()
            pygame.mixer.quit()
            os._exit(0)

        def on_volume_up():
            self.config['volume'] = min(1.0, self.config['volume'] + 0.1)
            print(f"音量: {self.config['volume']:.1f}")
            self.save_config()

        def on_volume_down():
            self.config['volume'] = max(0.0, self.config['volume'] - 0.1)
            print(f"音量: {self.config['volume']:.1f}")
            self.save_config()

        try:
            control_listener = keyboard.GlobalHotKeys({
                '<ctrl>+<alt>+k': on_toggle,
                '<ctrl>+<alt>+q': on_quit,
                '<ctrl>+<alt>+<up>': on_volume_up,    # 修正：使用<up>而不是up
                '<ctrl>+<alt>+<down>': on_volume_down # 修正：使用<down>而不是down
            })

            control_listener.daemon = True
            control_listener.start()
            return control_listener
        except Exception as e:
            logging.error(f"控制热键设置失败: {e}")
            print(f"❌ 控制热键设置失败: {e}")
            class DummyListener:
                def stop(self): pass
                def join(self): pass
            return DummyListener()

    def show_status(self):
        print(f"\n=== Ubuntu吐槽键盘状态 ===")
        print(f"系统: Ubuntu/Linux")
        print(f"吐槽开关: {'✅ 开启' if self.config['enabled'] else '❌ 关闭'}")
        print(f"音量: {self.config['volume']:.1f}")
        print(f"总吐槽次数: {self.stats['total_taunts']}")
        print(f"音频目录: {self.audio_dir}")
        print(f"音频系统: {'✅ 已初始化' if pygame.mixer.get_init() else '❌ 未初始化'}")
        print(f"预加载音频: {len(self.loaded_sounds)} 个")
        print(f"当前状态: {self.current_state.name}")
        print(f"上次状态: {self.last_state.name}")
        print(f"\n🐧 支持吐槽的按键:")
        print(f"  单个键: Tab, F5, Enter, CapsLock, 空格, Backspace")
        print(f"  组合键: Ctrl+S, Ctrl+C, Ctrl+V, Ctrl+Z, Ctrl+F, Ctrl+Q, Alt+F4, Ctrl+Alt+T")
        print(f"  连续键: 连续按Backspace {self.config['spam_threshold']} 次")
        print(f"\n🔧 控制快捷键:")
        print(f"  Ctrl+Alt+K: 切换吐槽开关")
        print(f"  Ctrl+Alt+↑: 音量增加")
        print(f"  Ctrl+Alt+↓: 音量减少")
        print(f"  Ctrl+Alt+Q: 退出程序")
        print("==========================\n")

    def start(self):
        self.running = True
    
        print("🎹 启动Ubuntu吐槽键盘...")
        print("🐧 专为Linux系统优化")
        print("🔄 使用状态匹配检测连续按键")
        
        self.show_status()
        print("🔊 测试播放启动音频...")
        self.play_taunt_and_wait('startup')
    
        try:
            print("🔄 启动键盘监听...")
            keyboard_listener = self.start_listening()
            
            print("🔄 启动控制热键监听...")
            control_listener = self.start_control_monitoring()
            
            print("✅ Ubuntu吐槽键盘已启动！")
            print("🎮 现在可以测试按键吐槽了～")
            print("💡 特别提示: 连续按删除键会触发特殊吐槽")
            
            try:
                while self.running:
                    time.sleep(1)
                    
                    if int(time.time()) % 300 == 0:
                        self.save_stats()
                        
            except KeyboardInterrupt:
                print("\n收到退出信号")
            finally:
                self.stop()
            
        except Exception as e:
            logging.error(f"启动过程中发生错误: {e}")
            print(f"❌ 启动过程中发生错误: {e}")
            import traceback
            traceback.print_exc()
            self.stop()

    def stop(self):
        self.running = False
        self.save_stats()
        self.save_config()
        pygame.mixer.quit()
        logging.info("Ubuntu吐槽键盘服务已停止")


def main():
    print("🎹 Ubuntu吐槽键盘 - Linux程序员的快乐源泉")
    print("=============================================")
    print("🔄 使用状态匹配检测连续按键")
    
    if os.name != 'posix':
        print("❌ 这个版本专为Ubuntu/Linux系统设计")
        return

    try:
        result = subprocess.run(['lsb_release', '-i'], capture_output=True, text=True)
        if 'Ubuntu' not in result.stdout and 'Debian' not in result.stdout:
            print("⚠️  这个程序主要针对Ubuntu/Debian系统优化")
    except:
        print("⚠️  无法检测Linux发行版，继续运行...")
    
    talking_keyboard = TalkingKeyboardUbuntu()
    
    try:
        talking_keyboard.start()
    except Exception as e:
        logging.error(f"程序运行错误: {e}")
        print(f"❌ 错误: {e}")


if __name__ == "__main__":
    main()